package com.cheesmo.nzb.client;

import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import com.cheesmo.nzb.codec.DecodingException;
import com.cheesmo.nzb.codec.SplitFileInputStream;
import com.cheesmo.nzb.codec.YEncDecoder;
import com.cheesmo.nzb.model.File;
import com.cheesmo.nzb.model.Group;
import com.cheesmo.nzb.model.NZB;
import com.cheesmo.nzb.model.Segment;
import com.cheesmo.nzb.util.NzbUtils;

public final class NzbClient
{
	private ClientConfig config;
	private ConnectionPool pool;

	public NzbClient(String[] args)
	{
		this.config = new ClientConfig();

		if (isModifyPrefs(args)) {
			this.config.editPrefs();
			return;
		}

		if (isResetPrefs(args)) {
			this.config.reset();
			return;
		}

		this.pool = new ConnectionPool(this.config);

		java.io.File file = new java.io.File(this.config.getDownloadDir());
		if (!file.exists()) {
			file.mkdirs();
			System.out.println("Creating download directory " + file);
		}

		file = new java.io.File(this.config.getCacheDir());
		if (!file.exists()) {
			file.mkdirs();
			System.out.println("Creating cache directory " + file);
		}

		String nzbPath = getNzbPath(args);
		if (nzbPath == null) {
			System.out.println("NZB file must be specified.\n");
			printUsage();
		} else if (!new java.io.File(nzbPath).canRead()) {
			System.out.println("Cannot read " + nzbPath);
		} else {
			start(nzbPath);
		}
	}

	private static boolean isModifyPrefs(String[] args)
	{
		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-config")) {
				return true;
			}
		}
		return false;
	}

	private static boolean isResetPrefs(String[] args)
	{
		for (int i = 0; i < args.length; i++) {
			if (args[i].equals("-reset")) {
				return true;
			}
		}
		return false;
	}

	private static String getNzbPath(String[] args) {
		for (int i = 0; i < args.length; i++) {
			if (!args[i].startsWith("-")) {
				return args[i];
			}
		}
		return null;
	}

	public void start(String nzbPath)
	{
		NZB nzb = getNzb(nzbPath);
		List<File> files = nzb.getFiles();
		int fileCount = 1;
		for (File file : files) {

			System.out.println("File " + fileCount + "/" + files.size() + " " + file.getSubject());
			List<String> segmentNames = new ArrayList<String>();
			List<DownloadThread> downloadThreads = new ArrayList<DownloadThread>();

			for (Segment seg : file.getSegments()) {
				String downloadName = file.getSubject().hashCode() + "_" + seg.getNumber() + ".yenc";

				DownloadThread thread = createDownloadSegThread(this.pool, ((Group) file.getGroups().get(0)).getName(), "<" + seg.getString() + ">", downloadName);
				thread.start();
				try
				{
					Thread.sleep(100L);
				} catch (InterruptedException localInterruptedException1) {
				}
				downloadThreads.add(thread);

				segmentNames.add(downloadName);
			}

			int segCount = 1;
			boolean failure = false;
			for (DownloadThread thread : downloadThreads) {
				try {

					thread.join();
					if (thread.getResult()) {
						System.out.println("\t" + segCount + "/" + file.getSegments().size() + " of " + file.getSubject());
					} else {
						System.err.println("\t" + segCount + "/" + file.getSegments().size() + " of " + file.getSubject() + " failed.");
						failure = true;
					}
					segCount++;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			if (!failure) {
				launchDecode(segmentNames);
			} else {
				System.err.println("Couldn't download all segments.  Skipping decode.");
				for (Iterator<String> j = segmentNames.iterator(); j.hasNext();)
				{
					new java.io.File(this.config.getCacheDir() + java.io.File.separator + j.next()).delete();
				}
			}

			fileCount++;
		}
	}

	private void launchDecode(List<String> segmentNames)
	{
		try
		{
			Collections.sort(segmentNames, new Comparator<String>()
			{
				@Override
				public int compare(String id1, String id2) {
					Integer segmentNumber1 = Integer.valueOf(Integer.parseInt(id1.substring(id1.indexOf("_") + 1, id1.indexOf("."))));
					Integer segmentNumber2 = Integer.valueOf(Integer.parseInt(id2.substring(id2.indexOf("_") + 1, id2.indexOf("."))));
					return segmentNumber1.compareTo(segmentNumber2);
				}

			});
			SplitFileInputStream sfis = new SplitFileInputStream(this.config.getCacheDir(), (String[]) segmentNames.toArray(new String[segmentNames.size()]), true);
			System.out.println("Decoding . . .");
			YEncDecoder decoder = new YEncDecoder(sfis, this.config.getDownloadDir());
			String fileDecoded = decoder.decode();

			if (fileDecoded != null)
				System.out.println("Decoding " + fileDecoded + " complete.");
			else
				System.out.println("Couldn't decode.");
		} catch (FileNotFoundException e)
		{
			e.printStackTrace();
		} catch (DecodingException e) {
			e.printStackTrace();
		}
	}

	private DownloadThread createDownloadSegThread(ConnectionPool pool, String group, String messageId, String downloadFilename) {
		return new DownloadThread(pool, group, messageId, downloadFilename);
	}

	private NZB getNzb(String path)
	{
		return NzbUtils.parseFile(path);
	}

	public static void main(String[] args)
	{
		new NzbClient(args);
	}

	public static void printUsage() {
		System.out.println("Usage: nzbclient [options] <NZB FILE>");
		System.out.println("Downloads the specified file.\n");
		System.out.println("  -reset    resets all config settings and runs interactive configuration tool.");
		System.out.println("  -config   modify config settings.");
	}
}
